package com.hos.check.ds;

import java.io.InputStream;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;

import com.hos.check.util.DBSetting;


public class DataSet {

	private static final Log log = LogFactory.getLog(DataSet.class);

	private Connection conn = null;

	private boolean toggleAutoCommit = false;

	private Statement stmt = null;

	private PreparedStatement prepstmt = null;

	private CallableStatement cstmt = null;

	public static final String DS_NAME = "idb";

	public static final String JDBC_VER = "jdbc";

	/**
	 * 获取本地连接数据源
	 * 
	 * @author 龚健
	 * @since 2010.8.27
	 */
	public DataSet() throws Exception {
		this(null);
	}

	/**
	 * 根据数据源名字构造器
	 */
	public DataSet(String dataSource) throws Exception {
		try {
			getDataSource(dataSource);
			stmt = conn.createStatement();
		} catch (Exception e) {
			log.error("DataSet init error: ", e);
			throw e;
		}
	}

	/**
	 * 根据数据源名字，SQL语句构造器
	 */
	public DataSet(String dataSource, String sql) throws Exception {
		try {
			getDataSource(dataSource);
			prepareStatement(sql);
		} catch (SQLException e) {
			log.error("DataSet init error: " + e);
			throw e;
		}
	}

	/**
	 * 根据数据源名字取得数据连接。
	 * 
	 * @param dataSource
	 *            - 数据连接配置标识。
	 */
	private void getDataSource(String dataSource) throws Exception {
		getJdbcConn(dataSource);
	}


	/**
	 * 根据数据源名字取得JDBC数据连接。
	 * 
	 * @param dataSource
	 *            - 数据连接配置标识。
	 */
	private void getJdbcConn(String dataSource) throws Exception {
		log.debug("dataSource::" + dataSource);
		try {
			if (!DBPoolManager.isInDBPool(dataSource)) {
//				
////				数据字典HIS
//				Sysdict dict=null;
////				唯一标记
//				dict.getCodekind();
////				数据库url
//				dict.getDescription();
////				所有驱动
//				dict.getExtend1();
////				用户名
//				dict.getExtend2();
////				密码
//				dict.getExtend3();
				Properties pt=DBSetting.loadHISProperty();
				
				String driver=pt.getProperty("connection.driverClass");
				String url=pt.getProperty("connection.url");
				String username=pt.getProperty("connection.userName");
				String password=pt.getProperty("connection.password");
				String alias=pt.getProperty("connection.alias");
//				ICompDbcpService dbs = (ICompDbcpService) SpringBeanUtil
//						.getBean("compDbcpService");
//				CompDbcp dbcp = (CompDbcp) dbs.getCompDbcpByName(dataSource);
				Map<String, String> map = new HashMap<String, String>();
				map.put("driver", driver);
				map.put("url", url);
				map.put("user", username);
				map.put("password", password);
				map.put("alias", alias);
				DBPoolManager.registerPool(map);
			}
			conn = DBPoolManager.getConnection(dataSource);
			toggleAutoCommit = conn.getAutoCommit();
		} catch (Exception e) {
			log.error("getJdbcConn(" + dataSource + ") error: " + e);
			throw e;
		}
	}

	/**
	 * 取得数据源连接。
	 * 
	 * @return connection - 取得数据库连接。
	 */
	public Connection getConnection() {
		return conn;
	}

	/**
	 * 执行预处理语句，并根据参数集，设置参数值。参数格式为 : + 参数名<br>
	 * 备注：SQL语句类似select * from table where id=:id中:id会被认为是参数。
	 * 
	 * @param sql
	 *            预处理语句
	 * @param params
	 *            需替换的参数集
	 * @throws SQLException
	 */
	public void setParametersInPreparation(String sql,
			Map<String, Object> params) throws SQLException {
		Assert.notNull(sql);
		// 记录替换后的SQL
		StringBuffer sqlBuff = new StringBuffer();
		// 记录需替换的参数集
		List<String> paramList = new ArrayList<String>();
		// 正则表达式
		Pattern pattern = Pattern.compile(":[^\\s=)<>,]+");
		Matcher matcher = pattern.matcher(sql);
		// 替换相应的参数为问号
		while (matcher.find()) {
			paramList.add(matcher.group(0).substring(1));
			matcher.appendReplacement(sqlBuff, "?");
		}
		matcher.appendTail(sqlBuff);
		// 预处理SQL
		prepareStatement(sqlBuff.toString());
		// 设置参数值
		for (int i = 0, length = paramList.size(); i < length; i++) {
			Object value = params.get(paramList.get(i));
			if (value instanceof String) {
				setString(i + 1, (String) value);
			} else if (value instanceof Float) {
				setFloat(i + 1, (Float) value);
			} else if (value instanceof Integer) {
				setInt(i + 1, (Integer) value);
			} else if (value instanceof Long) {
				setLong(i + 1, (Long) value);
			} else if (value instanceof java.sql.Date) {
				setDate(i + 1, (java.sql.Date) value);
			} else if (value instanceof java.sql.Timestamp) {
				setTimestamp(i + 1, (java.sql.Timestamp) value);
			} else {
				setString(i + 1, value.toString());
			}
		}
	}

	/**
	 * 开始事务。
	 */
	public void beginTran() throws SQLException {
		if (toggleAutoCommit) {
			if (log.isDebugEnabled()) {
				log.debug("禁止事务自动提交");
			}
			conn.setAutoCommit(false);
		}
	}

	/**
	 * 提交事务
	 */
	public void commit() throws SQLException {
		try {
			conn.commit();
		} finally {
			toggleAutoCommit();
		}
	}

	/**
	 * 回滚事务
	 */
	public void rollback() {
		try {
			conn.rollback();
		} catch (Exception ex) {
			log.debug(ex);
		} finally {
			toggleAutoCommit();
		}
	}

	/**
	 * 重置事务自动提交
	 */
	private void toggleAutoCommit() {
		try {
			if (toggleAutoCommit) {
				log.debug("重置事务自动提交");
				conn.setAutoCommit(true);
			}
		} catch (Exception e) {
			log.error("Could not toggle autocommit", e);
		}
	}

	/**
	 * 执行预处理语句。
	 * 
	 * @param sql
	 *            - 预处理SQL语句。
	 */
	public void prepareStatement(String sql) throws SQLException {
		prepstmt = conn.prepareStatement(sql);
	}

	/**
	 * 执行存储过程语句。
	 * 
	 * @param sql
	 *            - 存储过程SQL语句。
	 */
	public void prepareCall(String sql) throws SQLException {
		prepareCall(sql, false);
	}

	/**
	 * 执行存储过程语句。
	 * 
	 * @param concurChanged
	 *            - 结果集是否支持的光标的移动。
	 */
	public void prepareCall(String sql, boolean concurChanged)
			throws SQLException {
		if (concurChanged) {
			cstmt = conn.prepareCall(sql, ResultSet.TYPE_SCROLL_INSENSITIVE,
					ResultSet.CONCUR_READ_ONLY);
		} else {
			cstmt = conn.prepareCall(sql);
		}
	}

	/**
	 * 取得存储过程语句的对象。
	 * 
	 * @return cstmt - 预处理语句的对象。
	 */
	public PreparedStatement getPreparedStatement() {
		return prepstmt;
	}

	/**
	 * 给预处理语句设置字符型参数值。
	 * 
	 * @param index
	 *            - 参数字段索引号。
	 * @param value
	 *            - 参数值。
	 */
	public void setString(int index, String value) throws SQLException {
		prepstmt.setString(index, value);
	}

	/**
	 * 给预处理语句设置整型参数值。
	 * 
	 * @param index
	 *            - 参数字段索引号。
	 * @param value
	 *            - 参数值。
	 */
	public void setInt(int index, int value) throws SQLException {
		prepstmt.setInt(index, value);
	}

	/**
	 * 给预处理语句设置整型参数值，与setInt的区别是对于值为0用null处理，主要用于外键为空的设置。
	 * 
	 * @param index
	 *            - 参数字段索引号。
	 * @param value
	 *            - 参数值。
	 */
	public void setForKeyInt(int index, int value) throws SQLException {
		if (value <= 0) {
			prepstmt.setNull(index, 0);
		} else {
			setInt(index, value);
		}
	}

	/**
	 * 给预处理语句设置整型参数值，与setString的区别是对于值为""用null处理，主要用于外键为空的设置。
	 * 
	 * @param index
	 *            - 参数字段索引号。
	 * @param value
	 *            - 参数值。
	 */
	public void setForKeyString(int index, String value) throws SQLException {
		if (value.length() == 0) {
			prepstmt.setNull(index, 0);
		} else {
			setString(index, value);
		}
	}

	/**
	 * 给预处理语句设置整型参数值，与setInt的区别是对于值为0用null处理，主要用于外键为空的设置。
	 * 
	 * @param index
	 *            - 参数字段索引号。
	 * @param value
	 *            - 参数值。
	 */
	public void setForKeyInt(PreparedStatement pstmt, int index, int value)
			throws SQLException {
		if (value <= 0) {
			pstmt.setNull(index, 0);
		} else {
			pstmt.setInt(index, value);
		}
	}

	/**
	 * 给预处理语句设置整型参数值，与setString的区别是对于值为""用null处理，主要用于外键为空的设置。
	 * 
	 * @param index
	 *            - 参数字段索引号。
	 * @param value
	 *            - 参数值。
	 */
	public void setForKeyString(PreparedStatement pstmt, int index, String value)
			throws SQLException {
		if (value.length() == 0) {
			pstmt.setNull(index, 0);
		} else {
			pstmt.setString(index, value);
		}
	}

	/**
	 * 给预处理语句设置boolean型参数值。
	 * 
	 * @param index
	 *            - 参数字段索引号。
	 * @param value
	 *            - 参数值。
	 */
	public void setBoolean(int index, boolean value) throws SQLException {
		prepstmt.setBoolean(index, value);
	}

	/**
	 * 给预处理语句设置日期型参数值。
	 * 
	 * @param index
	 *            - 参数字段索引号。
	 * @param value
	 *            - 参数值。
	 */
	public void setDate(int index, Date value) throws SQLException {
		prepstmt.setDate(index, value);
	}

	/**
	 * 给预处理语句设置时间型参数值。
	 * 
	 * @param index
	 *            - 参数字段索引号。
	 * @param value
	 *            - 参数值。
	 */
	public void setTime(int index, Time value) throws SQLException {
		prepstmt.setTime(index, value);
	}

	/**
	 * 给预处理语句设置时间戳型参数值，包括datetime类型的字段赋值。
	 * 
	 * @param index
	 *            - 参数字段索引号。
	 * @param value
	 *            - 参数值。
	 */
	public void setTimestamp(int index, Timestamp value) throws SQLException {
		prepstmt.setTimestamp(index, value);
	}

	/**
	 * 给预处理语句设置long型参数值。
	 * 
	 * @param index
	 *            - 参数字段索引号。
	 * @param value
	 *            - 参数值。
	 */
	public void setLong(int index, long value) throws SQLException {
		prepstmt.setLong(index, value);
	}

	/**
	 * 给预处理语句设置float型参数值。
	 * 
	 * @param index
	 *            - 参数字段索引号。
	 * @param value
	 *            - 参数值。
	 */
	public void setFloat(int index, float value) throws SQLException {
		prepstmt.setFloat(index, value);
	}

	/**
	 * 给预处理语句设置二进制流的参数值。
	 * 
	 * @param index
	 *            - 参数字段索引号。
	 * @param value
	 *            - 参数值。
	 */
	public void setBinaryStream(int index, InputStream in, int length)
			throws SQLException {
		prepstmt.setBinaryStream(index, in, length);
	}

	/**
	 * 清除所有参数值。
	 */
	public void clearParameters() throws SQLException {
		prepstmt.clearParameters();
	}

	/**
	 * 取得预处理语句的对象。
	 * 
	 * @return prepstmt - 预处理语句的对象。
	 */
	public CallableStatement getCallableStatement() {
		return cstmt;
	}

	/**
	 * 取得语句的对象。
	 * 
	 * @return stmt - 语句的对象。
	 */
	public Statement getStatement() {
		return stmt;
	}

	/**
	 * 取得带有游标的语句。
	 * 
	 * @param typeScroll
	 *            - 移动方式。
	 * @param concurOpe
	 *            - 打开方式。
	 * @return 带有游标的语句对象。
	 */
	public Statement getStatement(int typeScroll, int concurOpe)
			throws SQLException {
		return conn.createStatement(typeScroll, concurOpe);
	}

	/**
	 * 查询结果记录集。
	 * 
	 * @param sql
	 *            - 待执行的SQL语句。
	 * @return 结果记录集。
	 */
	public ResultSet executeQuery(String sql) throws SQLException {
		if (stmt != null) {
			return stmt.executeQuery(sql);
		} else {
			return null;
		}
	}

	/**
	 * 查询结果记录集。
	 * 
	 * @return 结果记录集。
	 */
	public ResultSet executeQuery() throws SQLException {
		if (prepstmt != null) {
			return prepstmt.executeQuery();
		} else {
			return null;
		}
	}

	/**
	 * 更新结果记录集。
	 * 
	 * @param sql
	 *            - 待执行的SQL语句。
	 */
	public void executeUpdate(String sql) throws SQLException {
		if (stmt != null) {
			stmt.executeUpdate(sql);
		}
	}

	/**
	 * 更新结果记录集。
	 */
	public int executeUpdate() throws SQLException {
		if (prepstmt != null) {
			return prepstmt.executeUpdate();
		}
		return 0;
	}

	/**
	 * 取得刚增加的主键值。
	 * 
	 * @return 主键值。
	 */
	public int getGeneratedKey() throws SQLException {
		if (prepstmt != null) {
			ResultSet rs = prepstmt.getGeneratedKeys();
			if (rs.next()) {
				return rs.getInt(1);
			}
		}
		return 0;
	}

	/**
	 * 根据数据源名字关闭数据库连接。
	 * 
	 * @param dataSource
	 *            数据源名字
	 */
	public void close() {
		closeStmt();
		try {
			conn.close();
		} catch (Exception ex) {
			log.debug(ex);
		}
	}

	/**
	 * 关闭语句。
	 */
	public void closeStmt() {
		try {
			if (stmt != null) {
				stmt.close();
				stmt = null;
			}
			if (prepstmt != null) {
				prepstmt.close();
				prepstmt = null;
			}
			if (cstmt != null) {
				cstmt.close();
				cstmt = null;
			}
		} catch (Exception e) {
			log.debug("Statement close error: " + e);
		}
	}
}